package smartgrid;

import ETL.ETL;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.orm.PersistentException;
import smartgrid.exceptions.NoDataAvailableException;


public class MoneyStats extends Stats {

    public static final short NONE = 0;
    public static final short DATELINE = 1;
    public static final short DAYS = 2;
    public static final short WEEKS = 3;
    public static final short MONTHS = 4;
    public static final int SHOW_DAYS = 7;
    public static final int SHOW_MONTHS = 90;
    /**
     * Valor monetário total.
     */
    private double Total;

    public double getTotal() {
        return this.Total;
    }

    public void setTotal(double Total) {
        this.Total = Total;
    }

    public EltricStats getEletricStats(GregorianCalendar begin, GregorianCalendar end, Contract contract, short type) throws NoDataAvailableException{
        
            EltricStats stats = new EltricStats();
            GregorianCalendar startDate = new GregorianCalendar();
            GregorianCalendar endDate = new GregorianCalendar();

            startDate = (GregorianCalendar) begin.clone();
            endDate = (GregorianCalendar) end.clone();
            Date s = startDate.getTime();
            Date e = endDate.getTime();
            long dateDiff = e.getTime() - s.getTime();

            if (type == NONE || (type == WEEKS && (dateDiff / (1000 * 60 * 60 * 24)) < 7)
                    || (type == DATELINE && (dateDiff / (1000 * 60 * 60 * 24)) >= SHOW_DAYS)) {
                if (dateDiff / (1000 * 60 * 60 * 24) <= SHOW_DAYS) {
                    System.out.println("dataline");
                    stats = (this.getMoneyStatsHour(begin, end, contract));
                } else if (dateDiff / (1000 * 60 * 60 * 24) > SHOW_DAYS && dateDiff / (1000 * 60 * 60 * 24) <= SHOW_MONTHS) {
                    System.out.println("DIA");
                    stats = this.getMoneyStatsByMaterializedDay(begin, end, contract);
                } else {
                    System.out.println("MES");
                    stats = this.getMoneyStatsByMaterializedMonth(begin, end, contract);
                }
            } else {
                switch (type) {
                    case DATELINE:
                        stats = this.getMoneyStatsHour(begin, end, contract);
                        break;
                    case DAYS:
                        stats = this.getMoneyStatsByMaterializedDay(begin, end, contract);
                        break;
                    case WEEKS:
                        stats = this.getMoneyStatsByMaterializedWeeks(begin, end, contract);
                        break;
                    case MONTHS:
                        stats = this.getMoneyStatsByMaterializedMonth(begin, end, contract);
                        if (stats == null) {
                            if (end.get(Calendar.MONTH) == new GregorianCalendar().get(Calendar.MONTH) && end.get(Calendar.YEAR) == new GregorianCalendar().get(Calendar.YEAR)) {
                                if ((begin.get(Calendar.MONTH) - 1) == new GregorianCalendar().get(Calendar.MONTH)) {
                                    begin.set(Calendar.DAY_OF_MONTH, 1);
                                    stats = this.getMoneyStatsByMaterializedMonth(begin, end, contract);
                                } else {
                                    throw new NoDataAvailableException();
                                }
                            } else {
                                throw new NoDataAvailableException();
                            }
                        }
                        break;
                    default:
                        stats = null;
                        break;
                }
            }
            return stats;
    }

    public EltricStats getMoneyStatsByMaterializedDay(GregorianCalendar begin, GregorianCalendar end, Contract c) {
        EltricStats ele = new EltricStats();
        ele.setStartTime(begin);
        ele.setEndTime(end);
        ele.setInterval(DAYS);

        TreeSet<DataLine> linha = new TreeSet<DataLine>();

        DiaAgregadoCriteria dCrit;
        try {
            dCrit = new DiaAgregadoCriteria();
            dCrit.add(Restrictions.between("data", ETL.gCalenderToSqlDate(begin), ETL.gCalenderToSqlDate(end)));
            dCrit.add(Restrictions.eq("contract.id", c.getID()));

            DiaAgregado[] dias = DiaAgregado.listDiaAgregadoByCriteria(dCrit);
            DiaAgregado d;
            DataLine dltemp;
            Iterator<DiaAgregado> i = (Arrays.asList(dias)).iterator();
            while (i.hasNext()) {
                d = i.next();
                dltemp = new DataLine();
                dltemp.setContract(d.getContract());
                dltemp.setTariff(d.getContract().getTariff_CurrentTarif_role());
                dltemp.setHora((short) 0);
                dltemp.setMinuto((short) 0);
                dltemp.setUnit("kW");
                dltemp.setTimeStamp(d.getData());
                dltemp.setValue(d.getPrecoSUM());
                linha.add(dltemp);
            }

        } catch (PersistentException ex) {
            Logger.getLogger(EltricStatsManager.class.getName()).log(Level.SEVERE, null, ex);
        }
        ele.setDataLine_role(linha);
        return ele;
    }

    public EltricStats getMoneyStatsByMaterializedMonth(GregorianCalendar begin, GregorianCalendar end, Contract c) {
        EltricStats ele = new EltricStats();
        ele.setStartTime(begin);
        ele.setEndTime(end);
        ele.setInterval(MONTHS);
        TreeSet<DataLine> linha = new TreeSet<DataLine>();

        MesAgregadoCriteria dCrit;
        try {
            dCrit = new MesAgregadoCriteria();
            dCrit.add(Restrictions.between("data", ETL.gCalenderToSqlDate(begin), ETL.gCalenderToSqlDate(end)));
            dCrit.add(Restrictions.eq("contract.id", c.getID()));

            MesAgregado[] meses = MesAgregado.listMesAgregadoByCriteria(dCrit);
            MesAgregado d;
            DataLine dltemp;
            if (meses == null || Arrays.asList(meses).isEmpty()) {
                return null;
            }

            Iterator<MesAgregado> i = (Arrays.asList(meses)).iterator();
            while (i.hasNext()) {
                d = i.next();
                dltemp = new DataLine();
                dltemp.setContract(d.getContract());
                dltemp.setTariff(d.getContract().getTariff_CurrentTarif_role());
                dltemp.setHora((short) 0);
                dltemp.setMinuto((short) 0);
                dltemp.setUnit("kW");
                dltemp.setTimeStamp(d.getData());
                dltemp.setValue(d.getPrecoSUM());
                linha.add(dltemp);
            }
        } catch (PersistentException ex) {
            Logger.getLogger(EltricStatsManager.class.getName()).log(Level.SEVERE, null, ex);
        }

        ele.setDataLine_role(linha);
        return ele;
    }

    private static DataLine criaDataline(Contract contrato, Tariff tarrif, Date data, Double value) {
        DataLine dltemp = new DataLine();
        dltemp.setContract(contrato);
        dltemp.setUnit("kW");
        dltemp.setHora((short) 0);
        dltemp.setMinuto((short) 0);
        dltemp.setTimeStamp(data);
        dltemp.setTariff(tarrif);
        dltemp.setValue(value);
        return dltemp;
    }

    public EltricStats getMoneyStatsByMaterializedWeeks(GregorianCalendar begin, GregorianCalendar end, Contract c) {
        EltricStats ele = new EltricStats();
        ele.setStartTime(begin);
        ele.setEndTime(end);
        ele.setInterval(WEEKS);
        TreeSet<DataLine> linha = new TreeSet<DataLine>();

        SemanaAgregadaCriteria dCrit;
        try {
            dCrit = new SemanaAgregadaCriteria();
            dCrit.add(Restrictions.between("data", ETL.gCalenderToSqlDate(begin), ETL.gCalenderToSqlDate(end)));
            dCrit.add(Restrictions.eq("contract.id", c.getID()));
            dCrit.setProjection(Projections.projectionList().add(Projections.avg("domingoPrecoSUM")).add(Projections.avg("segundaPrecoSUM")).add(Projections.avg("tercaPrecoSUM")).add(Projections.avg("quartaPrecoSUM")).add(Projections.avg("quintaPrecoSUM")).add(Projections.avg("sextaPrecoSUM")).add(Projections.avg("sabadoPrecoSUM")).add(Projections.avg("domingoPrecoAVG")));
            Contract contrato = Contract.getContractByORMID(c.getID());
            DataLine dltemp;
            List<Object[]> rows = dCrit.list();

            if (rows != null && rows.get(0)[0] != null) {
                if (!(rows.isEmpty())) {
                    Object[] row = rows.get(0);
                    Tariff tarrif = null;
                    /* DOMINGO */
                    dltemp = criaDataline(contrato, tarrif, new java.sql.Date(begin.getTimeInMillis()), (Double) row[0]);
                    linha.add(dltemp);
                    /* SEGUNDA */
                    begin.add(Calendar.DAY_OF_YEAR, 1);
                    dltemp = new DataLine();
                    dltemp = criaDataline(contrato, tarrif, new java.sql.Date(begin.getTimeInMillis()), (Double) row[1]);
                    linha.add(dltemp);
                    /* TERCA */
                    begin.add(Calendar.DAY_OF_YEAR, 1);
                    dltemp = criaDataline(contrato, tarrif, new java.sql.Date(begin.getTimeInMillis()), (Double) row[2]);
                    linha.add(dltemp);
                    /* QUARTA */
                    begin.add(Calendar.DAY_OF_YEAR, 1);
                    dltemp = criaDataline(contrato, tarrif, new java.sql.Date(begin.getTimeInMillis()), (Double) row[3]);
                    linha.add(dltemp);
                    /* QUINTA */
                    begin.add(Calendar.DAY_OF_YEAR, 1);
                    dltemp = criaDataline(contrato, tarrif, new java.sql.Date(begin.getTimeInMillis()), (Double) row[4]);
                    linha.add(dltemp);
                    /* SEXTA */
                    begin.add(Calendar.DAY_OF_YEAR, 1);
                    dltemp = criaDataline(contrato, tarrif, new java.sql.Date(begin.getTimeInMillis()), (Double) row[5]);
                    linha.add(dltemp);
                    /* SABADO */
                    begin.add(Calendar.DAY_OF_YEAR, 1);
                    dltemp = criaDataline(contrato, tarrif, new java.sql.Date(begin.getTimeInMillis()), (Double) row[6]);
                    linha.add(dltemp);
                }
            }

        } catch (PersistentException ex) {
            Logger.getLogger(EltricStatsManager.class.getName()).log(Level.SEVERE, null, ex);
        }

        ele.setDataLine_role(linha);
        return ele;
    }

    private EltricStats getMoneyStatsHour(GregorianCalendar begin, GregorianCalendar end, Contract contract) {
        EltricStats stats = new EltricStats();

        DataLineCriteria dtLineCrit;
        smartgrid.PriceCriteria priceCrit;
        Price price;
        DataLine horaMoney;
        try {
            dtLineCrit = new DataLineCriteria();
            dtLineCrit.add(Restrictions.between("timeStamp", ETL.gCalenderToSqlDate(begin), ETL.gCalenderToSqlDate(end)));
            dtLineCrit.add(Restrictions.eq("contract.id", contract.getORMID()));
            DataLine[] horas = DataLine.listDataLineByCriteria(dtLineCrit);
            TreeSet<DataLine> linha = new TreeSet<DataLine>();

            for (DataLine hora : horas) {
                priceCrit = new PriceCriteria();
                priceCrit.add(Restrictions.eq("Tariff_Prices_role.id", hora.getTariff().getID()));
                priceCrit.add(Restrictions.sqlRestriction("((StartHour = " + hora.getHora() + " and StartMinute <= " + hora.getMinuto() + ")"
                        + " or StartHour < " + hora.getHora() + ") and "
                        + "((EndHour = " + hora.getHora() + " and EndMinute >= " + hora.getMinuto() + ")"
                        + " or EndHour > " + hora.getHora() + ")"));
                price = smartgrid.Price.loadPriceByCriteria(priceCrit);
                if (price != null) {
                    horaMoney = new DataLine();
                    horaMoney.setValue(hora.getValue() * price.getEuros());
                    horaMoney.setContract(contract);
                    horaMoney.setHora(hora.getHora());
                    horaMoney.setMinuto(hora.getMinuto());
                    horaMoney.setTariff(hora.getTariff());
                    horaMoney.setTimeStamp(hora.getTimeStamp());
                    horaMoney.setUnit(hora.getUnit());


                    linha.add(horaMoney);//TODO fazer new hora
                }
            }
            stats.setDataLine_role(linha);
            stats.setInterval(DATELINE); //TODO
            stats.setStartTime(begin);
            stats.setEndTime(end);

            return stats;
        } catch (PersistentException ex) {
            Logger.getLogger(EltricStatsManager.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    /**
     * Retorna o objecto serializado no formato JSon.
     * @return 
     */
    public String getJson() {
        throw new UnsupportedOperationException();
    }
}